// method-gadget.js v1.0
//
// This file implements some Method API functions based on open-social functionality. It may be not enough to convert any
// method into gadget, I tested it only with Risk GRC method. We may extend it to cover entire Method API and release as
// opensocial feature, but for now since it's not complete I just include it with Risk GRC gadget.
//
// USAGE:
// If you have some method and you want to transform it into gadget, you may follow few simple rules
//
// 0) If you are using data manipulation functions, like set_data or insert_array_item, gadget needs to require
//      <Require feature="osapi"/>
// 1) Include this script into gadget definition
//      <script src="method-gadget.js"></script>
// 2) Include method code into gadget definition
//      <script src="method.js"></script>
// 3) In method.js before actual method code, assign streamwork.methodGadget.initialData to the initial data of your method
//    (content of <InitialData> tag)


if (typeof(streamwork) == "undefined") {
    streamwork = {};
}
streamwork.methodGadget = {};
(function () {
    var $ = jQuery;
    var modules = {};

    function makeModule(name) {
        return {name:name};
    }

    streamwork.module = function (name) {
        var module = modules[name];
        if (module == null) {
            module = makeModule(name);
            modules[name] = module;
        }
        return module;
    };

    streamwork.getViewerId = function(){
        return streamwork.viewerId;
    };

    if (typeof(CSTAR) == "undefined"){
        CSTAR = {}
    }

    // currently I just hardcode these user settings, if gadget needs real values, instead of hardcoded, this should be separate feature
    CSTAR.current_user = {date_format_pattern: "M d, yy", date_format_symbols: {"short_months":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"long_months":["January","February","March","April","May","June","July","August","September","October","November","December"],"short_days":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"long_days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]}};

    var hardcodedStrings = {"lipstick.ok": "Ok", "lipstick.cancel": "Cancel"};

    if (CSTAR.t == null){
        CSTAR.t = function(str){
                        return hardcodedStrings[str] || ("["+str+"]");
                    };
    }



    var defaultCallbackName = { get_data:           'get_data_response',
                                set_data:           'data_updated',
                                get_array_item:     'get_data_response',
                                insert_array_item:  'data_inserted',
                                update_array_item:  'data_updated',
                                delete_array_item:  'data_deleted'};


    /**
     * Retrieves smartdata, calls func to modify it, saves appdata and calls done
     * @param func function that takes original value of smartContentData modifies it
     */
    function updateSmartdata(func,done){
        getSmartdata(function(data){
            func(data);
            osapi.appdata.update({userId:'@shared', data:{smartContentData:data}}).execute(done);
        });
    }

    /**
     * Retrieves entire smartdata and calls callback with it
     */
    function getSmartdata(callback){
        osapi.appdata.get({userId:'@shared',keys:["smartContentData"]}).execute(function(obj){
            var data = (obj && obj["@shared"]) ? obj["@shared"].smartContentData || streamwork.methodGadget.initialData : streamwork.methodGadget.initialData;
            callback(data);
        });
    }

    function pathAsArray(path){
        if (typeof(path) === "string"){
            return path.split(".");
        } else {
            return path;
        }
    }

    /**
     * Returns hash, with keys:
     *  value - value at the path specified, can be null
     *  container - hash containing the value, not null
     *  key - key used to lookup or assign value
     *  container[key] == value
     * @param obj
     * @param path
     */
    function resolvePath(obj,path){
        var i,container,key,newObj = obj;
        path = pathAsArray(path);

        for(i=0;i<path.length;i++){
            container = newObj;
            key = path[i];
            newObj = container[key];
            if (newObj === null && i+1 < path.length){
                newObj = {};
                container[key] = newObj;
            }
        }
        return {value: newObj, container: container, key: key};
    }
    // Implementation of data methods using appdata for @shared user. There may be some other methods, which are not implemented yet. Data is stored under @shared user appdata['smartContentData']
    // TODO: there are few fields that may be passed to these functions, that I don't implement for now. Like sanitize and before_id, event_details
    // Currently I use path, id and value
    var dataMethods = {
        get_data:function(data,callback){
            getSmartdata(function(smartData){
                callback({path: data.path, value:resolvePath(smartData, data.path).value});
            });
        },
        set_data: function(data, callback){
            updateSmartdata(function(smartData){ // update func
                var res = resolvePath(smartData, data.path);
                res.container[res.key] = data.value;
            }, function(){ // done func
                callback({path: data.path, value: data.value});
            })
        },
        get_array_item:function(data,callback){
            getSmartdata(function(smartData){
                var i,obj,arr = resolvePath(smartData, data.path);
                if (arr === null){
                    return null;
                }
                for(i=0;i<arr.length;i++){
                    obj = arr[i];
                    if (obj.id === data.id){
                        callback({path: data.path, id: data.id, value: obj.value});
                    }
                }
            });
        },
        insert_array_item: function(data,callback){
            updateSmartdata(function(smartData){ // update func
                var res = resolvePath(smartData, data.path);
                if(res.value == null){
                    res.value = res.container[res.key] = [];
                }
                res.value.push({id: data.id, value: data.value});
            }, function(){ // done func
                callback({path: data.path, value: data.value, id: data.id});
            })
        },
        update_array_item:function(data,callback){
            updateSmartdata(function(smartData){ // update func
                var i,obj,res = resolvePath(smartData, data.path);
                if(res.value == null){
                    res.value = res.container[res.key] = [];
                }
                for(i=0;i<res.value.length;i++){
                    obj = res.value[i];
                    if (obj.id === data.id){
                        obj.value = data.value;
                        return;
                    }
                }
            }, function(){ // done func
                callback({path: data.path, value: data.value, id: data.id});
            })
        },
        delete_array_item:function(data,callback){
            updateSmartdata(function(smartData){ // update func
                var i,obj,res = resolvePath(smartData, data.path);
                if(res.value == null){
                    res.value = res.container[res.key] = [];
                }
                for(i=0;i<res.value.length;i++){
                    obj = res.value[i];
                    if (obj.id === data.id){
                        res.value.splice(i,1);
                        return;
                    }
                }
            }, function(){ // done func
                callback({path: data.path, id: data.id});
            })
        }
    };

    function asFunc(funcOrStr){
        if (typeof(funcOrStr) == "function"){
            return funcOrStr;
        } else {
            return eval("("+funcOrStr+")");
        }
    }

    $(document.body).ready(function () {
        osapi.newBatch().add("owner", osapi.people.getOwner())
                        .add("viewer", osapi.people.getViewer())
                        .add("appdata", osapi.appdata.get({userId:'@shared', keys:["smartContentData"]})).execute(function(obj){

                var storedSmartContentData = obj.appdata["@shared"] ? obj.appdata["@shared"].smartContentData : null;
                streamwork.viewerId = obj.viewer.id;
                var smartContentData = storedSmartContentData || streamwork.methodGadget.initialData || {};
                $(document.body).append("<div id='method-gadget'></div>");
                        for (var name in modules) {
                            if (modules.hasOwnProperty(name)){
                                var module = modules[name];
                                var controller = module.methodClient("method-gadget", smartContentData, {itemCreatorUuid: obj.owner.id});
                                controller.clientChannel = {
                                    publish: function(msg){
                                        var func = dataMethods[msg.type];
                                        if (func){
                                            func(msg.data, controller[msg.data.return_msg || defaultCallbackName[msg.type]]);
                                        } else {
                                            alert("Unsupported data method: "+msg.type);
                                        }
                                    }
                                }
                            }
                        }
            });
    });
})();


